//////////////////////////////////////////////////////////////////////////////////
//	Copyright 2011 by Yosef Grabivker, ryuho@homenetworksoft.com
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <ifaddrs.h>

#include "def.h"
#include "sysdepapi.h"

#define EXEC_FLAGS wxEXEC_NODISABLE|wxEXEC_SYNC

static bool gbInsidecall=false;

struct CwxExecGuard
{
	CwxExecGuard()
	{
		if ( gbInsidecall ) THROW_EXC(cERWrongObjectState);
		
		gbInsidecall = true;
	}
	
	~CwxExecGuard()
	{
		gbInsidecall = false;
	}
};



CSysDepAPI::CSysDepAPI(void)
{
}

CSysDepAPI::~CSysDepAPI(void)
{
}

bool CSysDepAPI::IsLoadedAtStartUp()
{
	bool bRes(false);

	return bRes;
}

void CSysDepAPI::SetLoadAtStartUp(bool bVal)
{
	
}

///proc/net/route
std::string CSysDepAPI::GetDefGateWay()
{
	std::string strRet;
	
	char buf[4096];
	char interface[128];
	char *pBuf = (char *)buf;
	unsigned int lDest, lGateway, lFlags;
	
	memset( buf,0,sizeof(buf) );
	
	FILE* fp = fopen(DEF_RT_PATH, "r");
	
	if ( fp )
	{
		fread( buf, 1, sizeof(buf)-10, fp );
	
		while(1)
		{
			int nFnd = sscanf(pBuf, "%s\t%X\t%X\t%X",interface, &lDest, &lGateway, &lFlags );
			
			if ( 4==nFnd && !lDest && 3==lFlags )
			{
				struct sockaddr_in addr;
				addr.sin_addr.s_addr = lGateway;
				strRet = inet_ntoa(addr.sin_addr);
				fprintf(stderr, "Found gateway: %s\n", strRet.c_str());
				break;
			}
			
			pBuf = strchr (pBuf, '\n' );
			
			if ( !pBuf ) break;
			
			pBuf++;
		};
		
		fclose(fp);
	}
	
	return strRet;
}
////////////////////////////////////////////////////////////////////////////

//Get ip addresses list of current PC
IPLIST CSysDepAPI::GetAddressList()
{
	IPLIST list;
	
	struct ifaddrs * ifAddrStruct=NULL;
	void * tmpAddrPtr=NULL;
	char addressBuffer[INET_ADDRSTRLEN+1];

	getifaddrs( &ifAddrStruct );
	
	if ( ifAddrStruct )
	{
		while (ifAddrStruct!=NULL)
		{
			if ( ifAddrStruct->ifa_addr &&
				 ifAddrStruct->ifa_flags & IFF_UP &&
				 ifAddrStruct->ifa_addr->sa_family==AF_INET &&
				 NULL == strstr(ifAddrStruct->ifa_name, "lo") )
			{
				// check it is IP4 and not lo0
				// is a valid IP4 Address
				tmpAddrPtr=&((struct sockaddr_in *)ifAddrStruct->ifa_addr)->sin_addr;
				
				const char* szAddr = inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
				
				list.push_back(szAddr);
			}
			
			ifAddrStruct=ifAddrStruct->ifa_next;
		}
		
		freeifaddrs(ifAddrStruct);
		
		ifAddrStruct = NULL;
	}

	
	return list;
}

wxString CSysDepAPI::GetRelevantLocalIPAddress(const wxString& strRouterAddr)
{
	wxString str, strAddr;

	IPLIST lst = GetAddressList();
	std::string strRouterIP = GetDefGateWay();

	for(size_t i=0;i<lst.size();i++)
	{
		strAddr = wxString::FromAscii( lst[i].c_str() );

		if ( strAddr.Left(3)==strRouterAddr.Left(3) )
		{
			str = strAddr;
			break;
		}
	}

	return str;
}

wxString CSysDepAPI::GetLocalIPSettings()
{
	wxString strRes;
	static wxString strResSaved;
	
	wxArrayString output;
	
	if ( !gbInsidecall ) 
	{
		CwxExecGuard guard;
		
		wxExecute( wxT("ifconfig"), output, EXEC_FLAGS );  
	
		if ( !output.IsEmpty() )
		{
			for (size_t i=0;i<  output.GetCount();i++)
			{
				if (!strRes.IsEmpty())
					strRes+=wxT("\n");
				strRes+= output.Item(i); 
			}
		}
		
		strResSaved = strRes;
	}
	else
	{
		strRes = strResSaved;
	}

	return strRes;
}

wxString CSysDepAPI::GetLocalRoutingTable()
{
	wxString strRes;
	static wxString strResSaved;
	
	if ( !gbInsidecall ) 
	{
		CwxExecGuard guard;
	
		wxArrayString output;
		wxExecute( wxT("route"), output, EXEC_FLAGS );  
	
		if ( !output.IsEmpty() )
		{
			for (size_t i=0;i<  output.GetCount();i++)
			{
				if (!strRes.IsEmpty())
					strRes+=wxT("\n");
				strRes+= output.Item(i); 
			}
		}
		
		strResSaved = strRes;
	}
	else
	{
		strRes = strResSaved;
	}

	return strRes;
}

//Is detect server app is possible for UDP?
PROTOLIST CSysDepAPI::GetUDPProtoList()
{
	PROTOLIST lst;

	return lst;
}

PROTOLIST CSysDepAPI::GetTCPProtoList(const wxString& strRouterIP)
{
	PROTOLIST lst;
	static PROTOLIST lstSaved;
	std::string strIPRCon = (const char*)GetRelevantLocalIPAddress( strRouterIP ).ToAscii();
	
	if ( !gbInsidecall ) 
	{
		CwxExecGuard guard;
		
		wxArrayString output;
		wxExecute( wxT("netstat -l -t -n -p -4"), output, EXEC_FLAGS );  
	
		if ( !output.IsEmpty() )
		{
			for (size_t i=0;i<  output.GetCount();i++)
			{
				char* szLine = strdup( output.Item(i).ToAscii() );
				const char* szTokens=" \t";
				char * pch;
	
				if ( szLine )
				{
					unsigned short	wPort = 0;
					wxString strIP;
					wxString strProgname;
					
					pch = strtok (szLine, szTokens);
					while (pch != NULL)
					{
						char* szPos = strchr(pch, ':');
						
						//Parse IP/Port
						if ( szPos && !wPort )
						{
							wPort = (unsigned short)atol(szPos+1);
							*szPos='\0';
							
							if ( strstr(pch, "127.0")==NULL )
							{
								if ( strstr(pch, "0.0.0.0")==NULL )
								{	
									strIP = wxString::FromAscii(pch);
								}
								else
								{
									strIP = wxString::FromAscii( strIPRCon.c_str() ); 
								}
							}
						}
						
						szPos = strchr(pch, '/');
						
						if ( szPos )
						{
							*szPos='\0';
							
							strProgname = wxString::FromAscii(szPos+1);
							
							break;
						}
						
						pch = strtok (NULL, szTokens);
					}
					
					if ( !strProgname.IsEmpty() &&
						 !strIP.IsEmpty() &&
						 wPort  )
					{
						PROTOENTRY entry;
												
						entry.proto = cPortFrorwardProtoTCPIP;
						entry.strIP = strIP;
						entry.wPort = wPort;
						entry.strOwnerName = strProgname;
						entry.strProcName = strProgname;
						
						//Forward only router clear addr
						
						#ifndef DEBUG
						if ( strIPRCon == (const char*)strIP.ToAscii() )
						#endif
						{
							lst.push_back(entry);
						}
					}
								
					free(szLine);
				}
			}
			
			lstSaved = lst;
		}
	}
	else
	{
		lst = lstSaved;
	}

	return lst;
}

#define MAX_IFS 64

MACLIST CSysDepAPI::GetPCInterfacesMACList()
{
	MACLIST list;
	
	struct ifreq *ifr, *ifend;
	struct ifreq ifreq;
	struct ifconf ifc;
	struct ifreq ifs[MAX_IFS];
	int SockFD;


	SockFD = socket(AF_INET, SOCK_DGRAM, 0);

    ifc.ifc_len = sizeof(ifs);
    ifc.ifc_req = ifs;
    if (ioctl(SockFD, SIOCGIFCONF, &ifc) >=0)
    {
	    ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
	    
	    for (ifr = ifc.ifc_req; ifr < ifend; ifr++)
		{
	        if (ifr->ifr_addr.sa_family == AF_INET)
	        {
	            strncpy(ifreq.ifr_name, ifr->ifr_name,sizeof(ifreq.ifr_name));
	            if (ioctl (SockFD, SIOCGIFHWADDR, &ifreq) < 0)
	            {
	            	break;
	            }
	            
	            char buf[128];
	            sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
			      (int) ((unsigned char *) &ifreq.ifr_hwaddr.sa_data)[0],
			      (int) ((unsigned char *) &ifreq.ifr_hwaddr.sa_data)[1],
			      (int) ((unsigned char *) &ifreq.ifr_hwaddr.sa_data)[2],
			      (int) ((unsigned char *) &ifreq.ifr_hwaddr.sa_data)[3],
			      (int) ((unsigned char *) &ifreq.ifr_hwaddr.sa_data)[4],
			      (int) ((unsigned char *) &ifreq.ifr_hwaddr.sa_data)[5]);
	            
	            MACENTRY entry;
	            entry.strMAC = buf;
	            list.push_back(entry);
	        }
	    }
	    
		close(SockFD);
    }

	return list;
}

